/******************************************************
Compile : gcc -O3 -o enumerate enumerate.c io.c check.c
******************************************************/

#include "infer.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void print_help ()
{
	printf ("Usage          : ./enumerate inputfile patternfile trigger model\n");
	printf ("                (./enumerate input2 A 1 3)\n\n");
	printf ("trigger        : 1 first node is a trigger node\n");
	printf ("model          : 0 SIM; 1 SIM + Reliable Trans; 2 SIM + Convergence; 3 SIM + Reliable Trans + Convergence\n");
	printf ("Note           : This program will generate all possible processes for given dynamics order and process pattern by enumeration.\n\n");
}

int compare (timeorder *p1, timeorder *p2)
{
	int n1, n2;

	if (p1->n1 > p1->n2)
		n1 = p1->n1;
	else
		n1 = p1->n2;

	if (p2->n1 > p2->n2)
		n2 = p2->n1;
	else
		n2 = p2->n2;

	return n1 - n2;
}

int *read_inputfile2 (char *filename, struct infer *infer)
{
	FILE *fp;
	char buf[256];
	int i, j, N, L, *pattern;

	N = infer->N; 

	fp = fopen (filename, "r");
	fscanf (fp, "%*s%*s%*s%d", &L);
	infer->L = L;
	fgets (buf, 256, fp);

	pattern = calloc (N * L, sizeof (int));
	for (i = 0; i < L; i++)
	{
		fgets (buf, 256, fp);
		for (j = 0; j < N; j++)
		{
			if (buf[2 * j] == '1')
				pattern[i * N + j] = 1;
			else if (buf[2 * j] == '0')
				pattern[i * N + j] = 0;
			else
				pattern[i * N + j] = -1;
		}
	}

	fclose (fp);

	return pattern;
}

void do_enumeration (struct infer *infer, int *pattern, int pathway[infer->L][infer->N], int i, int j, int flips, int timeorder[infer->N][infer->flips[infer->N]], int m)
{
	int N, L;

	N = infer->N;
	L = infer->L;

	if (i == N)
	{
		int mode;

		if (infer->trigger == 1)
			mode = 2;
		else
			mode = 1;

		if (network_inferred (N, L, pathway, mode, 0, infer->model) == 1)
			print_pathway (N, L, pathway);

		return;
	}

	/* no more flips */
	if (flips > infer->flips[i])
		return;

	/* no quickflips */
	if ((infer->model == 1 || infer->model == 3) && j >= 3 + infer->trigger)
	{
		if (pathway[j - 1][i] != pathway[j - 2][i] && pathway[j - 2][i] != pathway[j - 3][i])
			return;
	}

	if (j == L)
	{
		int k, order;

		/* check number of flips */
		if (flips != infer->flips[i])
			return;

		/* check the order of the flips */
		order = 0;
		for (k = 1; k < L; k++)
		{
			if (pathway[k][i] != pathway[k - 1][i])
			{
				timeorder[i][order] = k;
				order ++;
			}
		}

		int n1, n2, flip1, flip2;

		for (k = m; k < infer->M; k++)
		{
			n1 = infer->timeorder[k].n1;
			n2 = infer->timeorder[k].n2;

			if (n1 > i || n2 > i)
			{
				m = k;
				break;
			}

			flip1 = infer->timeorder[k].flip1 - 1;
			flip2 = infer->timeorder[k].flip2 - 1;

			if (infer->timeorder[k].type == 0)
			{
				if (timeorder[n1][flip1] > timeorder[n2][flip2])
					return;
			}
			else if (infer->timeorder[k].type == 1)
			{
				if (timeorder[n1][flip1] >= timeorder[n2][flip2])
					return;
			}
		}

		do_enumeration (infer, pattern, pathway, i + 1, 0, 0, timeorder, m);
		return;
	}

	if (pattern[j * N + i] == -1)
	{
		int flips2;

		pathway[j][i] = 0; flips2 = flips;
		if (j > 0 && pathway[j][i] != pathway[j - 1][i]) flips2 ++;
		do_enumeration (infer, pattern, pathway, i, j + 1, flips2, timeorder, m);

		pathway[j][i] = 1; flips2 = flips;
		if (j > 0 && pathway[j][i] != pathway[j - 1][i]) flips2 ++;
		do_enumeration (infer, pattern, pathway, i, j + 1, flips2, timeorder, m);
	}
	else
	{
		pathway[j][i] = pattern[j * N + i];
		if (j > 0 && pathway[j][i] != pathway[j - 1][i]) flips ++;
		do_enumeration (infer, pattern, pathway, i, j + 1, flips, timeorder, m);
	}
}

int main (int argc, char *argv[])
{
	struct infer infer;

	if (argc == 1)
	{
		print_help ();
		exit (0);
	}

	read_inputfile (argv[1], &infer);
	qsort (infer.timeorder, infer.M, sizeof (timeorder), compare);
	infer.trigger = atoi (argv[3]);
	infer.model = atoi (argv[4]);

	int *pattern;

	pattern = read_inputfile2(argv[2], &infer);

	int pathway[infer.L][infer.N], timeorder[infer.N][infer.flips[infer.N]];

	do_enumeration (&infer, pattern, pathway, 0, 0, 0, timeorder, 0);
}
